﻿using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Text;
using XinLife.Core.Helper;
using System.Reflection;
using System.Web;
using XinLife.Core.Extention;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using System.Xml;
using Newtonsoft.Json;

namespace XinLife.Common.Filters
{
    /// <summary>
    /// 记录请求日志
    /// </summary>
    public class OperateLogAttribute : ActionFilterAttribute
    {
        private ILogger<OperateLogAttribute> _logger;

        public OperateLogAttribute(ILogger<OperateLogAttribute> logger)
        {
            _logger = logger;
        }

        private static readonly string key = "enterTime";
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            if (SkipLogging(context))//是否该类标记为跳过
            {
                base.OnActionExecuting(context);
                return;
            }
            //模型绑定
            var form = context.ActionDescriptor.Parameters.FirstOrDefault(r => r.BindingInfo?.BindingSource == BindingSource.Form);
            //获取请求参数
            foreach (var item in context.ActionDescriptor.Parameters)
            {
                
            }
            //记录进入请求的时间
            context.HttpContext.Items[key] = DateTime.Now.ToBinary();
            base.OnActionExecuting(context);
        }
        /// <summary>
        /// 在请求执行完后 记录请求的数据以及返回数据
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override void OnActionExecuted(ActionExecutedContext context)
        {
            if (context.HttpContext.Items.TryGetValue(key, out object beginTime))
            {
                DateTime time = DateTime.FromBinary(Convert.ToInt64(beginTime));
                HttpRequest request = context.HttpContext.Request;

                //判断FromBody是否存在
                var isBody = context.ActionDescriptor.Parameters.Any(r => r.BindingInfo?.BindingSource == BindingSource.Body);
                //请求
                var descriptor = (ControllerActionDescriptor)context.ActionDescriptor;

                StringBuilder log = new StringBuilder(System.Environment.NewLine);
                log.AppendLine($"Request:{request.Method}:{request.Path.ToString() + HttpUtility.UrlDecode(request.QueryString.ToString())}");
                if (request.Method == "POST") log.AppendLine("Request ContentType：" + request.ContentType);
                log.AppendLine("Request Ip：" + context.HttpContext?.Connection?.RemoteIpAddress?.ToString());
                log.AppendLine("Request User：" + AspNetUser.User?.cName);
                log.AppendLine("Request ActionName：" + GetActionName(descriptor.ControllerName, descriptor.ActionName));//接口中文名称

                if (request.ContentType == null || request.ContentLength == null || !request.ContentType.Contains("multipart/form-data"))
                {
                    string req = GetRequestValues(context);
                    if (req.IsNotEmpty())
                    {
                        log.AppendLine("Request Body：" + req);
                    }
                }
                log.AppendLine("Request StartTime：" + time.ToString("yyyy-MM-dd HH:mm:ss.fff"));
                log.AppendLine("Response EndTime：" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));
                log.AppendLine($"Response Body({(DateTime.Now - time).TotalMilliseconds}ms):");
                log.AppendLine(GetResponseValues(context));
                log.AppendLine(System.Environment.NewLine);
                _logger.LogInformation(log.ToString());
            }
            base.OnActionExecuted(context);
        }

        /// <summary>
        /// 读取request 的提交内容
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public string GetRequestValues(ActionExecutedContext context)
        {
            if (context.HttpContext.Request.ContentLength != null)
            {
                //可以实现多次读取Body
                context.HttpContext.Request.EnableBuffering();

                if (context.HttpContext.Request.ContentType != null && context.HttpContext.Request.ContentType.Contains("application/x-www-form-urlencoded"))
                {
                    var formDictionary = context.HttpContext.Request.Form.ToDictionary(x => x.Key, x => x.Value.ToString());
                    return formDictionary.ToJson();
                }
                else
                {
                    var requestReader = new StreamReader(context.HttpContext.Request.Body);
                    var requestContent = requestReader.ReadToEnd();
                    context.HttpContext.Request.Body.Position = 0;
                    return requestContent;
                }
            }
            return "";
        }

        /// <summary>
        /// 读取action返回的result
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public string GetResponseValues(ActionExecutedContext context)
        {
            if (context.Exception != null)
            {
                return context.Exception.Message.ToString();
            }
            if (context.Result is ObjectResult objectResult)
            {
                return objectResult.Value.ToJson();
            }
            if (context.Result is FileResult fileResult)
            {
                return "下载文件：" + fileResult.FileDownloadName;
            }
            return "";
        }
        /// <summary>
        /// 判断类和方法头上的特性是否要进行Action拦截
        /// </summary>
        /// <param name="actionExecutingContext"></param>
        /// <returns></returns>
        private static bool SkipLogging(ActionExecutingContext actionExecutingContext)
        {
            var Endpoint = actionExecutingContext.HttpContext.GetEndpoint();
            var action = Endpoint?.Metadata.GetMetadata<WriteLogAttribute>();
            var controllerType = Endpoint!.Metadata.GetMetadata<ControllerActionDescriptor>()!.ControllerTypeInfo;

            // 获取控制器级别的元数据
            //var actionDescriptor = (ControllerActionDescriptor)actionExecutingContext.ActionDescriptor;
            //var controllerMetadata = actionDescriptor.EndpointMetadata;
            return action?.IsWriteLog == false || controllerType.GetCustomAttribute<WriteLogAttribute>()?.IsWriteLog == false;
        }


        /// <summary>
        /// 获取接口的中文名称
        /// </summary>
        /// <param name="Controller">控制器</param>
        /// <param name="Action">方法名称</param>
        /// <returns>接口的中文名称是通过Swagger生成的XML文档中的注释文字，如果没有写注释那么将无法获取到该文本内容</returns>
        private string GetActionName(string Controller, string Action)
        {
            try
            {
                string name = string.Empty;
                using (FileStream fs = new FileStream(AppDomain.CurrentDomain.BaseDirectory + System.Reflection.Assembly.GetExecutingAssembly().ManifestModule.Name.Replace("dll", "xml"), FileMode.Open, FileAccess.Read))
                {
                    XmlDocument doc = new XmlDocument();
                    doc.Load(fs);
                    XmlNode xnl1 = doc.DocumentElement;
                    doc.Clone();
                    foreach (XmlNode xn1 in xnl1)
                    {
                        XmlElement xe1 = (XmlElement)xn1;
                        if (xe1.LocalName.ToString() == "members")
                        {
                            XmlNodeList xnl2 = xe1.ChildNodes;
                            foreach (XmlNode xn2 in xnl2)
                            {
                                XmlElement xe2 = (XmlElement)xn2;
                                if (xe2.GetAttribute("name").ToString().IndexOf(Controller) > 0)
                                {
                                    if (xe2.GetAttribute("name").IndexOf(Action) > 0)
                                    {
                                        name = xe2.InnerText.Trim();
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    xnl1.Clone();
                }
                return name;
            }
            catch (Exception ex)
            {
                return "";
            }
        }

    }


    /// <summary>
    /// 记录日志的特性。
    /// </summary>
    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true)]
    public class WriteLogAttribute : Attribute
    {
        public WriteLogAttribute(bool isWriteLog = true)
        {
            this.IsWriteLog = isWriteLog;
        }

        /// <summary>
        /// 是否记录日志
        /// </summary>
        public bool IsWriteLog { get; set; }
    }
}